package com.jinyang.jetpackdemo.activity

import android.os.Bundle
import androidx.appcompat.app.AppCompatActivity
import com.google.gson.*
import com.google.gson.annotations.Expose
import com.google.gson.annotations.SerializedName
import com.google.gson.annotations.Since
import com.google.gson.annotations.Until
import com.google.gson.reflect.TypeToken
import com.jinyang.jetpackdemo.R
import com.jinyang.jetpackdemo.bean.Test
import java.io.StringReader
import java.io.StringWriter
import java.lang.reflect.Field
import java.lang.reflect.Modifier
import java.util.*
import com.google.gson.TypeAdapter
import com.google.gson.stream.JsonReader
import com.google.gson.stream.JsonWriter
import java.io.IOException
import com.google.gson.GsonBuilder

import com.google.gson.Gson
import java.lang.reflect.Type

import com.google.gson.JsonDeserializationContext

import com.google.gson.JsonElement

import com.google.gson.JsonDeserializer
import java.text.NumberFormat
import com.google.gson.TypeAdapterFactory

import com.google.gson.annotations.JsonAdapter
import com.jinyang.jetpackdemo.util.gson.BaseHttpResponse
import com.jinyang.jetpackdemo.util.gson.GsonDefaultAdapterFactory

import com.jinyang.jetpackdemo.util.gson.KotlinAdapterFactory2
import com.jinyang.jetpackdemo.util.gson.GsonUtils
import java.lang.reflect.ParameterizedType


class GsonActivity : AppCompatActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_gson)
        //Gson提供了fromJson() 和toJson() 两个直接用于解析和生成的方法，前者实现反序列化，后者实现了序列化。
        val gson = Gson()
        //1.1 解析基本数据类型
        val i = gson.fromJson("100", Int::class.java) //100
        val d = gson.fromJson("99.99", Double::class.java) //99.99
        val b = gson.fromJson("true", Boolean::class.java) // true
        val s = gson.fromJson("jin", String::class.java) // String
        log("i=$i,d=$d,b=$b,s=$s")
        //1.2 生成基本数据类型
        val jsonI: String = gson.toJson(100) // 100
        val jsonD: String = gson.toJson(99.99) // 100
        val jsonB: String = gson.toJson(false) // false
        val jsonS: String = gson.toJson("String") //"String"
        log("jsonI=$jsonI,jsonD=$jsonD,jsonB=$jsonB,jsonS=$jsonS")
        //2. 解析及生成自定义对象
        //在方法中定义的类gson解析和生成都是null
//        data class UserInfo(var name: String? , var age: Int?,var emailAddress:String? )
        val user = UserInfo(name = "222", age = 18, "xxx@163.com")
        log("user=${user}")
        val userStr = gson.toJson(user)
        log("userStr:$userStr")
        val user2: UserInfo? = gson.fromJson(userStr, UserInfo::class.java)
        log("user2=${user2}")
        //3. 属性重命名 @SerializedName 注解的使用
        val user3: UserInfo? =
            gson.fromJson("{\"user_age\":18,\"user_name\":\"333\"}", UserInfo::class.java)
        log("user3=$user3")
        val user4: UserInfo? =
            gson.fromJson("{\"u_age\":18,\"user_name\":\"444\"}", UserInfo::class.java)
        log("user4=$user4")
        //属性备选名：当多种情况同时出时，以最后一个出现的值为准。
        val user5: UserInfo? = gson.fromJson(
            "{\"Age\":18,\"user_age\":19,\"u_age\":20,\"user_name\":\"555\"}",
            UserInfo::class.java
        )
        log("user5=$user5")
        //3.2 POJO与JSON的字段映射规则
        //GsonBuilder提供了FieldNamingStrategy接口和setFieldNamingPolicy和setFieldNamingStrategy 两个方法;
        //@SerializedName注解拥有最高优先级，在加有@SerializedName注解的字段上FieldNamingStrategy不生效！
        val gson6 = GsonBuilder()
//            .setFieldNamingPolicy(FieldNamingPolicy.IDENTITY)//emailAddress
//            .setFieldNamingPolicy(FieldNamingPolicy.LOWER_CASE_WITH_DASHES)//email-address
//            .setFieldNamingPolicy(FieldNamingPolicy.LOWER_CASE_WITH_UNDERSCORES)//email_address
//            .setFieldNamingPolicy(FieldNamingPolicy.UPPER_CAMEL_CASE)//EmailAddress
            .setFieldNamingPolicy(FieldNamingPolicy.UPPER_CAMEL_CASE_WITH_SPACES)//Email Address
            .create()
        log("setFieldNamingPolicy:${gson6.toJson(UserInfo3())}")

        //setFieldNamingStrategy与Gson提供的FieldNamingStrategy接口配合使用 实现自己的规则
        val gson7 = GsonBuilder()
            .setFieldNamingStrategy(FieldStrategy.AllUpperCaseStrategy())
            .setFieldNamingStrategy(FieldStrategy.GsonKeyValueCaseStrategy())
            .create()
        log("setFieldNamingStrategy:${gson7.toJson(UserInfo4())}")
        //4. 泛型
        val jsonArray = gson.toJson(arrayOf(user2, user3, user4, user5))
        val userArr = gson.fromJson(jsonArray, Array<UserInfo>::class.java)
        log("userArr=${gson.toJson(userArr)}")
        val userList: List<UserInfo> = gson.fromJson(jsonArray, List::class.java) as List<UserInfo>
        log("userList=$userList")
        val userList2: List<UserInfo?>? =
            gson.fromJson(jsonArray, object : TypeToken<List<UserInfo?>?>() {}.type)
        log("userList2=$userList2")
        //5.1 Gson的流式反序列化
        //(1) 手动的方式: 使用stream包下的JsonReader类来手动实现反序列化，和Android中使用pull解析XML是比较类似的
        val user6 = UserInfo(null, null, null, null)
        val reader = JsonReader(StringReader(userStr))
        reader.beginObject()
        while (reader.hasNext()) {
            when (reader.nextName()) {
                "name" ->
                    user6.name = reader.nextString()
                "age" ->
                    user6.age = reader.nextInt()
                "emailAddress" ->
                    user6.emailAddress = reader.nextString()
                "temp" ->
                    user6.temp = reader.nextString()
                "date" ->
                    user6.date = Date(reader.nextString())
            }
        }
        reader.endObject()
        log("user6=$user6")
        //(2) 自动方式最终都是通过JsonReader来实现的，如果第一个参数是String类型，那么Gson会创建一个StringReader转换成流操作
        //public <T> T fromJson(String json, Type typeOfT) throws JsonSyntaxException {
        //    if (json == null) {
        //      return null;
        //    }
        //    StringReader reader = new StringReader(json);
        //    T target = (T) fromJson(reader, typeOfT);
        //    return target;
        //}
        //5.2 Gson的流式序列化
        //(1) 手动
        val user7 = UserInfo("777", 17, "7777@qq.com")
//        val writer = JsonWriter(OutputStreamWriter(System.out))
        val stringWriter = StringWriter()
        val writer = JsonWriter(stringWriter)
        writer.beginObject()
            .name("name").value(user7.name)
            .name("age").value(user7.age)
            .name("emailAddress").nullValue()
            .endObject()
        writer.flush()
        log("user7=$stringWriter")
        //(2) 自动
        // PrintStream(System.out) 、StringBuilder、StringBuffer和*Writer都实现了Appendable接口。
        // gson.toJson(user,System.out)
        val str1 = gson.toJson(user)
        //public String toJson(Object src, Type typeOfSrc) {
        //    StringWriter writer = new StringWriter();
        //    toJson(src, typeOfSrc, writer);
        //    return writer.toString();
        //}
        //6. 使用GsonBuilder
        val gsonDIY = GsonBuilder() //各种配置
            //序列化null/导出null值
            .serializeNulls()
            // 设置日期时间格式，另有2个重载方法
            // 在序列化和反序化时均生效
            .setDateFormat("yyyy-MM-dd")
            // 禁此序列化内部类
            .disableInnerClassSerialization()
            //生成不可执行的Json, 其实就是多了 )]}' 这4个字符
            .generateNonExecutableJson()
            //禁止转义html标签
            .disableHtmlEscaping()
            //格式化输出
            .setPrettyPrinting()
            .create() //生成配置好的Gson
        val user8 = UserInfo("888", 18)
        val user8Str = gsonDIY.toJson(user8)
        log("user8 toJson:${user8Str}")
        log("fromJson user8Str:${gsonDIY.fromJson(user8Str, UserInfo::class.java)}")
        //7. 字段过滤的几种方法
        //(1) 基于@Expose注解：需要导出的字段上加上@Expose 注解，不导出的字段不加
        //@Expose //@Expose提供了两个属性，且都有默认值
        //@Expose(deserialize = true,serialize = true) //序列化和反序列化都都生效，等价于上一条
        //@Expose(deserialize = true,serialize = false) //反序列化时生效
        //@Expose(deserialize = false,serialize = true) //序列化时生效
        //@Expose(deserialize = false,serialize = false) // 和不写注解一样
        //在使用Gson时也不能只是简单的new Gson()了
        val gson3 = GsonBuilder()
            .excludeFieldsWithoutExposeAnnotation()
            .create()
        gson3.toJson(user8)
        log("user8 toJson:${gson3.toJson(user8)}")
        //(2) 基于版本
        // Gson在对基于版本的字段导出提供了两个注解 @Since 和 @Until,
        // 和GsonBuilder.setVersion(Double)配合使用。@Since 和 @Until都接收一个Double值
        // 使用方法：当前版本(GsonBuilder中设置的版本) 大于等于Since的值时该字段导出，小于Until的值时该该字段导出
        val user9 = UserInfo2("999", 19, "9999@qq.com")
        log("user9 version 1.0:" + GsonBuilder().setVersion(1.0).create().toJson(user9))
        log("user9 version 4.0:" + GsonBuilder().setVersion(4.0).create().toJson(user9))
        log("user9 version 9.0:" + GsonBuilder().setVersion(9.0).create().toJson(user9))
        val user9Str = "{\"age\":19,\"emailAddress\":\"9999@qq.com\",\"name\":\"999\"}"
        log("user9Str version 1.0:" + GsonBuilder().setVersion(1.0).create().fromJson(user9Str, UserInfo2::class.java))
        log("user9Str version 4.0:" + GsonBuilder().setVersion(4.0).create().fromJson(user9Str, UserInfo2::class.java))
        log("user9Str version 9.0:" + GsonBuilder().setVersion(9.0).create().fromJson(user9Str, UserInfo2::class.java))
        //(3) 基于访问修饰符
        val gson4 = GsonBuilder()
            //排除了final,static,private
            .excludeFieldsWithModifiers(Modifier.FINAL, Modifier.STATIC, Modifier.PRIVATE)
            .create()
        log(gson4.toJson(Test()))
        //(4) 基于策略（自定义规则）
        //addSerializationExclusionStrategy , addDeserializationExclusionStrategy 分别针对序列化和反序化
        val gson5 = GsonBuilder()
            .addSerializationExclusionStrategy(object : ExclusionStrategy {
                override fun shouldSkipField(f: FieldAttributes): Boolean {
                    // 这里作判断，决定要不要排除该字段,return true为排除
                    if ("emailAddress" == f.name) return true //按字段名排除
                    val expose = f.getAnnotation(Expose::class.java)
                    return expose != null && !expose.deserialize //按注解排除:
                }

                override fun shouldSkipClass(clazz: Class<*>): Boolean {
                    // 直接排除某个类 ，return true为排除,例如排除Boolean.kt和Boolean.java
                    return clazz == Boolean::class || clazz == Boolean::class.java
                }
            })
            .create()
        log("UserInfo3:${gson5.toJson(UserInfo3())}")
        log("Test:${gson5.toJson(Test())}")
        //8. TypeAdapter
        //用于接管某种类型的序列化和反序列化过程，包含两个注要方法 write(JsonWriter,T) 和 read(JsonReader)
        // 其它的方法都是final方法并最终调用这两个抽象方法。
            //为User注册TypeAdapter
        val gson8 = GsonBuilder()
            .registerTypeAdapter(UserInfo3::class.java, UserInfoTypeAdapter())
            .create()
        log(gson8.toJson(UserInfo3()))
        log(gson8.fromJson("{\"name\":\"ljy\",\"age\":20,\"email_address\":\"xx@qq.com\",\"email\":\"qqq@qq.com\"}", UserInfo3::class.java))
        //将字符串 "24" 转成int 的24,但如果有些情况下给你返了个空字符串怎么办
        //8.2 JsonSerializer与JsonDeserializer
        //只接管序列化的过程就用 JsonSerializer ，只接管反序列化的过程就用 JsonDeserializer
        //TypeAdapter与 JsonSerializer、JsonDeserializer对比
        //TypeAdapter:效率高,内存占用小,支持Stream API
        val gson9 = GsonBuilder()
            .registerTypeAdapter(Number::class.java, NumTypeAdapter())
//            .registerTypeAdapter(Int::class.java,  NumJsonSerializer())
//            .registerTypeAdapter(Long::class.java,  NumJsonSerializer())
//            .registerTypeAdapter(Float::class.java,  NumJsonSerializer())
//            .registerTypeAdapter(Double::class.java,  NumJsonSerializer())
//            .registerTypeAdapter(Int::class.java, NumJsonDeserializer())
//            .registerTypeAdapter(Long::class.java, NumJsonDeserializer())
//            .registerTypeAdapter(Float::class.java, NumJsonDeserializer())
//            .registerTypeAdapter(Double::class.java, NumJsonDeserializer())
            //registerTypeAdapter与registerTypeHierarchyAdapter的区别：
            //registerTypeAdapter：支持泛型,不支持继承
            //registerTypeHierarchyAdapter:不支持泛型,支持继承
            .registerTypeHierarchyAdapter(Number::class.java, NumJsonSerializer())
            .registerTypeHierarchyAdapter(Number::class.java, NumJsonDeserializer())
            .create()
        log(gson9.toJson(100, Number::class.java))
        log(gson9.toJson(6.66, Number::class.java))
        log(gson9.toJson(111111111111111111, Number::class.java))
        log(gson9.fromJson("\"\"", Number::class.java))
        log(gson9.fromJson("", Number::class.java))
        log(gson9.fromJson("88.88", Number::class.java))
        log(gson9.fromJson("55", Number::class.java))
        log(gson9.fromJson("111111111111111111", Number::class.java))

        log(gson9.toJson(100, Int::class.java))
        log(gson9.toJson(6.66, Double::class.java))
        log(gson9.toJson(111111111111111111, Long::class.java))
        log(gson9.fromJson("\"\"", Number::class.java))
        log(gson9.fromJson("", Double::class.java))
        log(gson9.fromJson("88.88", Double::class.java))
        log(gson9.fromJson("55", Int::class.java))
        log(gson9.fromJson("111111111111111111", Long::class.java))

        //8.3 TypeAdapterFactory
        //用于创建TypeAdapter的工厂类，通过对比Type，确定有没有对应的TypeAdapter，没有就返回null，
        // 与GsonBuilder.registerTypeAdapterFactory配合使用
        //KotlinAdapterFactory2
        val gson10 = GsonBuilder()
            .registerTypeAdapterFactory(GsonDefaultAdapterFactory())
            .create()
        //8.4 @JsonAdapter注解
        //用在POJO类上的，接收一个参数，且必须是TypeAdapter,TypeAdapterFactory，JsonSerializer或JsonDeserializer其中之一
        //使用时不用再使用 GsonBuilder去注册UserTypeAdapter了
        log("UserInfo5:${Gson().toJson(UserInfo5())}")

        //8.5 服务器返回的数据中data字段类型不固定，比如请求成功data是一个List,不成功的时候是String类型，这样前端在使用泛型解析的时候，怎么去处理呢？
        val gson11 = GsonBuilder().registerTypeHierarchyAdapter(
            MutableList::class.java,
            JsonDeserializer { json, typeOfT, context ->
                if (json.isJsonArray) {
                    log("isJsonArray")
                    val array = json.asJsonArray
                    val itemType: Type =
                        (typeOfT as ParameterizedType).actualTypeArguments[0]
                    val list: MutableList<Any?> = mutableListOf()
                    for (i in 0 until array.size()) {
                        val element = array[i]
                        val item = context.deserialize<Any>(element, itemType)
                        list.add(item)
                    }
                    list
                } else {
                    log("返回空List")
                    Collections.EMPTY_LIST
                }
            }).create()
        log(gson11.toJson(listOf(UserInfo3())))
        val type = object : TypeToken<List<UserInfo3?>?>() {}.type
        log(gson11.fromJson("[{\"age\":20,\"emailAddress\":\"xx@qq.com\",\"height\":180,\"man\":true,\"name\":\"ljy\"}]", type))
        log(gson11.fromJson("{}", type))
        log(gson11.fromJson("暂无数据", type))

        //泛型封装
        val baseHttpResponse1=BaseHttpResponse(111,"aaa",UserInfo3("ljy",18))
        val strResponse1:String=GsonUtils.toJson(baseHttpResponse1)
        log("fromJsonObject:${GsonUtils.fromJson2Object(strResponse1,UserInfo3::class.java)}")

        val list=mutableListOf(UserInfo3("ljy",18),UserInfo3("qwer",19))
        log("fromJson(json,Type):${GsonUtils.fromJson<List<UserInfo3>>(GsonUtils.toJson(list),GsonUtils.getListType(UserInfo3::class.java))}")
        val baseHttpResponse2=BaseHttpResponse<List<UserInfo3>>(111,"aaa",list)
        val strResponse2:String=GsonUtils.toJson(baseHttpResponse2)
        log("fromJsonArray:${GsonUtils.fromJson2Array(strResponse2,UserInfo3::class.java)}")

        val map= mutableMapOf("key1" to UserInfo3("ljy",18),"key2" to UserInfo3("qwer",19))
        log("fromJson(json,Type):${GsonUtils.fromJson<Map<String,UserInfo3>>(GsonUtils.toJson(map),GsonUtils.getMapType(String::class.java,UserInfo3::class.java))}")
        val baseHttpResponse3=BaseHttpResponse<Map<String,UserInfo3>>()
        baseHttpResponse3.code=111
        baseHttpResponse3.message="aaa"
        baseHttpResponse3.data=map
        val strResponse3:String=GsonUtils.toJson(baseHttpResponse3)
        log("fromJsonMap:${GsonUtils.fromJson2Map(strResponse3,String::class.java,UserInfo3::class.java)}")

        fun1(Boolean.javaClass)
        fun1(Boolean::class.java)
        fun1(Boolean::class.javaPrimitiveType)
        fun1(Boolean::class.javaObjectType)
    }

    fun <T> fun1(classOfT: Class<T>?) {
        log("classOfT:$classOfT")
    }

    //  @JsonAdapter(NumJsonSerializer::class)
//  @JsonAdapter(NumJsonDeserializer::class)
//  @JsonAdapter(NumTypeAdapter::class)
    @JsonAdapter(KotlinAdapterFactory2::class)
    data class UserInfo5(
        var name: String = "ljy",
        private var email: String? = "xxx@qq.com",
        private val mobile: String? = "12315",
    )

    class NumJsonSerializer : JsonSerializer<Number> {
        override fun serialize(
            src: Number?,
            typeOfSrc: Type?,
            context: JsonSerializationContext?
        ): JsonElement {
            log("typeOfSrc:$typeOfSrc")
            return JsonPrimitive(src.toString())
        }
    }

    class NumJsonDeserializer : JsonDeserializer<Number> {
        override fun deserialize(
            json: JsonElement?,
            typeOfT: Type?,
            context: JsonDeserializationContext?
        ): Number {
            return try {
                log("typeOfT:$typeOfT")
                json?.asNumber ?: -1
            } catch (e: Exception) {
                log("e:${e}")
                -1
            }
        }

    }

    class NumTypeAdapter : TypeAdapter<Number>() {
        override fun write(out: JsonWriter, value: Number) {
            log("numTypeAdapter.write:$value")
            out.value(value.toString())
        }

        override fun read(`in`: JsonReader): Number {
            return try {
                val str = `in`.nextString()
                log("numTypeAdapter.read:$str")
                if (str == null || str.isEmpty()) {
                    return -1
                }
                NumberFormat.getInstance().parse(str) ?: -1
            } catch (e: Exception) {
                log("e:$e")
                -1
            }
        }
    }

    class UserInfoTypeAdapter : TypeAdapter<UserInfo3?>() {
        @Throws(IOException::class)
        override fun write(out: JsonWriter?, value: UserInfo3?) {
            out?.run {
                value?.let {
                    beginObject()
                    name("name").value(it.name)
                    name("age").value(it.age)
                    name("emailAddress").value(it.emailAddress)
                    endObject()
                }
            }
        }

        @Throws(IOException::class)
        override fun read(`in`: JsonReader): UserInfo3 {
            val user = UserInfo3()
            `in`.beginObject()
            while (`in`.hasNext()) {
                when (`in`.nextName()) {
                    "name" -> user.name = `in`.nextString()
                    "age" -> user.age = `in`.nextInt()
                    "email", "email_address", "emailAddress" -> user.emailAddress =
                        `in`.nextString()
                }
            }
            `in`.endObject()
            return user
        }
    }

    data class UserInfo4(
        var name: String = "ljy",
        @GsonKeyValue(prefix = "QQ_")
        private var email: String? = "a@a.com",
        @GsonKeyValue(prefix = "CN_", suffix = "_AN")
        private val mobile: String? = "12315",
        @GsonKeyValue(value = "desc_info", prefix = "CN_", suffix = "_AN")
        private val extra: String? = "qwerty asdf"
    )

    data class UserInfo3(
        var name: String = "ljy",
        @Expose(deserialize = false)
        var age: Int = 20,
        var height: Int = 180,
        var man: Boolean = true,
        var emailAddress: String = "xx@qq.com",
    )

    data class UserInfo2(
        var name: String?,
        @Since(3.0)
        var age: Int?,
        @Until(6.0)
        var emailAddress: String? = null,
    )

    data class UserInfo(
        //属性重命名, toJson后的字符串中的属性名会以value中的值为准
        @Expose @SerializedName(value = "name", alternate = ["user_name"]) var name: String?,
        //为字段提供备选属性名，当多种情况同时出时，以最后一个出现的值为准
        @Expose @SerializedName("age", alternate = ["user_age", "u_age", "Age"]) var age: Int?,
        @Expose @SerializedName("emailAddress") var emailAddress: String? = null,
        @Expose var date: Date? = Date(),
        var temp: String? = "没啥用的temp"
    )
}

@Target(AnnotationTarget.FIELD)
@Retention(AnnotationRetention.RUNTIME)
annotation class GsonKeyValue(
    val value: String = "",
    val prefix: String = "",
    val suffix: String = ""
)

class FieldStrategy {
    /**
     * 采用全部大写的形式
     */
    class AllUpperCaseStrategy : FieldNamingStrategy {
        override fun translateName(f: Field): String {
            return f.name.uppercase(Locale.getDefault())
        }
    }

    /**
     * 添加前置 和 后置 注解
     */
    class GsonKeyValueCaseStrategy : FieldNamingStrategy {
        override fun translateName(f: Field): String {
            val gkv: GsonKeyValue? = f.getAnnotation(GsonKeyValue::class.java)
            return if (gkv != null) {
                if (gkv.value.isNotEmpty()) {
                    gkv.prefix + gkv.value + gkv.suffix
                } else {
                    gkv.prefix + f.name + gkv.suffix
                }
            } else {
                f.name
            }
        }
    }
}
